package com.nstskj.study.netty.tcp.netty.protocol.definition.scan;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.nstskj.study.netty.tcp.netty.protocol.annotation.MessageCommandAnnotation;
import com.nstskj.study.netty.tcp.netty.protocol.definition.ProtocolMsgBeanDefinition;
import com.nstskj.study.netty.tcp.netty.protocol.definition.ProtocolMsgFieldDefinition;
import com.nstskj.study.netty.tcp.netty.protocol.definition.annotation.MessageField;
import com.nstskj.study.netty.tcp.netty.protocol.enums.IotDeviceProtocolCmdEnum;
import com.nstskj.study.netty.tcp.netty.protocol.message.in.msg.base.AbstractInputNetTcpMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ZhouChuGang
 * @version 1.0
 * @project nstskj-study-netty-tcp-spring
 * @date 2021/4/20 21:31
 * @Description 协议输入消息扫描器
 */
@Slf4j
public class ProtocolInputMsgScan implements Serializable {

    private static final long serialVersionUID = 1L;

    private final Map<Short, ProtocolMsgBeanDefinition> msgBeanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 需要扫描的路径
     */
    private final String basePack;

    public ProtocolInputMsgScan(String basePack) {
        this.basePack = basePack;
    }

    /**
     * 得到包扫描的信息
     *
     * @return
     */
    public Map<Short, ProtocolMsgBeanDefinition> getMsgBeanDefinitionMap() throws Exception {
        this.msgBeanDefinitionMap.clear();
        this.scan();
        return this.msgBeanDefinitionMap;
    }

    /**
     * 扫描指定的包，并把数据填充到map中
     */
    private void scan() throws Exception {
        log.info("开始扫描包路径 {}", this.basePack);
        Assert.notBlank(this.basePack, "包扫描路径不能为空");
        HashSet<BeanDefinition> definitions = new HashSet<>();
        //用spring提供的扫描器
        final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        //加入注解过滤器
        scanner.addIncludeFilter(new AnnotationTypeFilter(MessageCommandAnnotation.class));
        //包扫描
        StringUtils.commaDelimitedListToSet(this.basePack).forEach(str -> {
            Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(this.basePack);
            if (CollUtil.isNotEmpty(beanDefinitions)) {
                definitions.addAll(beanDefinitions);
            }
        });

        for (BeanDefinition beanDefinition : definitions) {
            //这里强转 用于得到类注解信息
            ScannedGenericBeanDefinition definition = (ScannedGenericBeanDefinition) beanDefinition;
            //这里可以得到bean的类名称
            String beanClassName = definition.getBeanClassName();
            //得到bean的class
            Class<?> aClass = ClassUtils.forName(beanClassName, Thread.currentThread().getContextClassLoader());
            //得到注解元数据
            AnnotationMetadata annotationMetadata = definition.getMetadata();
            if (annotationMetadata.hasAnnotation(MessageCommandAnnotation.class.getName()) && AbstractInputNetTcpMessage.class.isAssignableFrom(aClass)) {
                //把注解的源数据转换为map
                AnnotationAttributes annotationAttributes =
                        AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(MessageCommandAnnotation.class.getName(), false));
                IotDeviceProtocolCmdEnum iotDeviceProtocolCmdEnum = (IotDeviceProtocolCmdEnum) annotationAttributes.get("value");
                //得到命令码
                short cmd = (short) iotDeviceProtocolCmdEnum.getCmd();
                //最小长度
                int cmdDataMinLen = iotDeviceProtocolCmdEnum.getCmdDataMinLen();
                ProtocolMsgBeanDefinition protocolMsgBeanDefinition = new ProtocolMsgBeanDefinition();
                protocolMsgBeanDefinition.setCmdDataMinLen(cmdDataMinLen);
                protocolMsgBeanDefinition.setAbstractInputNetTcpMessageClass((Class<? extends AbstractInputNetTcpMessage>) aClass);
                protocolMsgBeanDefinition.setCmd(cmd);
                List<ProtocolMsgFieldDefinition> fieldDefinitions = getFieldDefinitions(aClass);
                protocolMsgBeanDefinition.setFieldDefinitions(fieldDefinitions);
                //放入缓存
                this.msgBeanDefinitionMap.put(cmd, protocolMsgBeanDefinition);
            }
        }
    }

    /**
     * 得到字段定义
     *
     * @param aClass
     * @return
     */
    private List<ProtocolMsgFieldDefinition> getFieldDefinitions(Class<?> aClass) {
        List<ProtocolMsgFieldDefinition> fieldDefinitionList = new ArrayList<>();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(MessageField.class)) {
                MessageField annotation = declaredField.getAnnotation(MessageField.class);
                ProtocolMsgFieldDefinition fieldDefinition = new ProtocolMsgFieldDefinition();
                fieldDefinition.setClassName(declaredField.getType().getName());
                fieldDefinition.setFieldName(declaredField.getName());
                fieldDefinition.setFieldStartIndex(annotation.startIndex());
                fieldDefinition.setFieldTypeEnums(annotation.fieldType());
                //如果长度为注解指定则用注解的，否则为枚举给出
                int length = annotation.fieldType().getLen() == -1 ? annotation.length() : annotation.fieldType().getLen();
                fieldDefinition.setFieldLen(length);
                fieldDefinition.setLittleEndian(annotation.isLittleEndian());
                fieldDefinitionList.add(fieldDefinition);
            }
        }
        //保证解析的顺序，考虑存在子报文的可能
        Collections.sort(fieldDefinitionList);
        return fieldDefinitionList;
    }
}
